/**
	Pulsar engine. Additional include file.
	Date & time management (include OS specific functions).

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.
		
	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

struct TimeT;
struct DateTime;

/*
	Universal datetime structure
*/
struct DateTime
{
	/// milliseconds (0...999)
	pl_int16 msecond;
	/// seconds (0...59)
	pl_int8 second;
	/// minutes (0...59)
	pl_int8 minute;
	/// hours (0...23)
	pl_int8 hour;
	/// days (1...31)
	pl_int8 day;
	/// month (1...12)
	pl_int8 month;
	/// year (1995, 2003, etc.)
	pl_int16 year;

	DateTime() { }
	DateTime(int year_, int month_, int day_, int hour_, int minute_, int second_, int msecond_ = 0)
	{
		set(year_, month_, day_, hour_, minute_, second_, msecond_);
		return;
	}
	DateTime(const pl_char* src, const pl_char** end = NULL, const pl_char* format = NULL)
	{
		set(src, end, format);
		return;
	}
	explicit DateTime(time_t time)
	{
		set(time);
		return;
	}
	explicit DateTime(const TimeT& time)
	{
		set(time);
		return;
	}
	explicit DateTime(const struct tm& time)
	{
		set(time);
		return;
	}

	/// Compare dates
	inline bool operator < (const DateTime& d) const;
	inline bool operator <= (const DateTime& d) const;
	inline bool operator > (const DateTime& d) const;
	inline bool operator >= (const DateTime& d) const;
	inline bool operator == (const DateTime& d) const;
	inline bool operator != (const DateTime& d) const;

	/// Add
	const DateTime operator + (const DateTime& d) const;
	inline const DateTime& operator += (const DateTime& d);

	/// Subtract
	const DateTime operator - (const DateTime& d) const;
	inline const DateTime& operator -= (const DateTime& d);

	/// Shift
	inline const DateTime& nextDay(int count = 1);
	inline const DateTime& nextMonth(int count = 1);
	inline const DateTime& nextYear(int count = 1);
	inline const DateTime& nextHour(int count = 1);
	inline const DateTime& nextMinute(int count = 1);
	inline const DateTime& nextSecond(int count = 1);

	inline const DateTime& prevDay(int count = 1);
	inline const DateTime& prevMonth(int count = 1);
	inline const DateTime& prevYear(int count = 1);
	inline const DateTime& prevHour(int count = 1);
	inline const DateTime& prevMinute(int count = 1);
	inline const DateTime& prevSecond(int count = 1);

	/// Validate date
	bool isValid() const;
	/// Distance (in days)
	static int diffDays(const DateTime& d1, const DateTime& d2);
	/// Distance (in minutes)
	static pl_int64 diffSeconds(const DateTime& d1, const DateTime& d2);
	/// Number of days in month
	static pl_uint8 dayInMonth(pl_uint8 month, pl_uint16 year);
	/// Number of days in year
	static pl_uint16 dayInYear(pl_uint16 year);
	/// Calculate day of week
	int dayOfWeek() const;
	/// Get GMP offset in seconds
	static time_t getUtsOffset();

	time_t get() const;
	void get(struct tm& time) const;

	/// Set the current time
	void setNow();
	/// Translate from string to date
	bool set(const pl_char* src, const pl_char** end = NULL, const pl_char* format = NULL);
	/// Translate from time_t
	void set(time_t time);
	void set(const TimeT& time);
	/// Translate from struct tm
	void set(const struct tm& time);
	/// Translate from string to date
	void set(int year_, int month_, int day_, int hour_, int minute_, int second_, int msecond_ = 0)
	{
		year    = (pl_uint16)year_;
		month   = (pl_uint8)month_;
		day     = (pl_uint8)day_;
		minute  = (pl_uint8)minute_;
		second  = (pl_uint8)second_;
		msecond = (pl_uint16)msecond_;
		return;
	}
	/// Format date to string
	void format(pl_char* dest, const pl_char* templateText = NULL) const;
};

/*
	Safe wrapper over time_t type
*/
struct TimeT
{
	time_t	_time;

	TimeT() { }
	TimeT(int year, int month, int day, int hour, int minute, int second, int msecond = 0)
	{
		set(year, month, day, hour, minute, second, msecond);
		return;
	}
	TimeT(const DateTime& dt)
	{
		set(dt);
		return;
	}
	TimeT(const pl_char* src, const pl_char** end = NULL, const pl_char* format = NULL)
	{
		set(src, end, format);
	}
	explicit TimeT(time_t time) :
	_time(time)
	{
		return;
	}
	explicit TimeT(const struct tm& time)
	{
		set(time);
		return;
	}

	/// Set the current time
	void setNow();
	/// Translate from string to date
	bool set(const pl_char* src, const pl_char** end = NULL, const pl_char* format = NULL)
	{
		DateTime dt;
		if (!dt.set(src, end, format))
		{
			return false;
		}
		set(dt);
		return true;
	}
	/// Translate from time_t
	void set(time_t time)
	{
		_time = time;
		return;
	}
	void set(const DateTime& dt);
	/// Translate from struct tm
	void set(const struct tm& time);
	/// Translate from string to date
	void set(int year, int month, int day, int hour, int minute, int second, int msecond = 0)
	{
		DateTime dt(year, month, day, hour, minute, second, msecond);
		set(dt);
		return;
	}

	/// Get offset from GMT to local time in seconds
	int getUtcOffset();
};

/**
	Compare dates (less)
*/
inline bool DateTime::operator < (const DateTime& d) const
{
	if (year != d.year)       { return year < d.year; }
	if (month != d.month)     { return month < d.month; }
	if (day != d.day)         { return day < d.day; }
	if (hour != d.hour)       { return hour < d.hour; }
	if (minute != d.minute)   { return minute < d.minute; }
	if (second != d.second)   { return second < d.second; }
	if (msecond != d.msecond) { return msecond < d.msecond; }
	return false;
}

/**
	Compare dates (less or equal)
*/
inline bool DateTime::operator <= (const DateTime& d) const
{
	if (*this == d)
	{
		return true;
	}
	return *this < d;
}

/**
	Compare dates (great)
*/
inline bool DateTime::operator > (const DateTime& d) const
{
	if (year != d.year)       { return year > d.year; }
	if (month != d.month)     { return month > d.month; }
	if (day != d.day)         { return day > d.day; }
	if (hour != d.hour)       { return hour > d.hour; }
	if (minute != d.minute)   { return minute > d.minute; }
	if (second != d.second)   { return second > d.second; }
	if (msecond != d.msecond) { return msecond > d.msecond; }
	return false;
}

/**
	Compare dates (great or equal)
*/
inline bool DateTime::operator >= (const DateTime& d) const
{
	if (*this == d)
	{
		return true;
	}
	return *this > d;
}

/**
	Compare dates (equal)
*/
inline bool DateTime::operator == (const DateTime& d) const
{
	if (year == d.year && month == d.month && day == d.day && hour == d.hour && minute == d.minute && second == d.second && msecond == d.msecond)
	{
		return true;
	}
	return false;
}

/**
	Compare dates (not equal)
*/
inline bool DateTime::operator != (const DateTime& d) const
{
	return !(*this == d);
}

inline const DateTime& DateTime::operator += (const DateTime& d)
{
	*this = *this + d;
	return *this;
}

/// Subtract
inline const DateTime& DateTime::operator -= (const DateTime& d)
{
	*this = *this - d;
	return *this;
}

/// Shift
inline const DateTime& DateTime::nextDay(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, count, 0, 0, 0);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::nextMonth(int count /*= 1*/)
{
	DateTime dtTemp(0, count, 0, 0, 0, 0);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::nextYear(int count /*= 1*/)
{
	DateTime dtTemp(year, 0, 0, 0, 0, 0);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::nextHour(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, count, 0, 0);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::nextMinute(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, 0, count, 0);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::nextSecond(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, 0, 0, count);
	*this += dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevDay(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, count, 0, 0, 0);
	*this -= dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevMonth(int count /*= 1*/)
{
	DateTime dtTemp(0, count, 0, 0, 0, 0);
	*this -= dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevYear(int count /*= 1*/)
{
	DateTime dtTemp(year, 0, 0, 0, 0, 0);
	*this -= dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevHour(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, count, 0, 0);
	*this -= dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevMinute(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, 0, count, 0);
	*this -= dtTemp;
	return *this;
}

inline const DateTime& DateTime::prevSecond(int count /*= 1*/)
{
	DateTime dtTemp(0, 0, 0, 0, 0, count);
	*this -= dtTemp;
	return *this;
}

} // namespace pl


#if defined(PL_WINDOWS) || defined(PL_WINDOWS64)

#include <pulsar/inc/win/pulsar_datetime_win.h>

#elif defined(PL_LINUX)

// Does nothing

#else
#error "Unknown OS"
#endif

//
// End of file 'pulsar_datetime.h'
//
